<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>交通灯</title>
    <style>
      #wrap {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      .dd {
        display: flex;
        gap: 10px;
      }
      .deng {
        width: 50px;
        height: 50px;
        border-radius: 9999px;
        background-color: #d9d9d9;
      }
      .time {
        margin-top: 20px;
      }
      .dd.red .red {
        background-color: red;
      }
      .dd.yellow .yellow {
        background-color: yellow;
      }
      .dd.green .green {
        background-color: green;
      }
    </style>
  </head>
  <body>
    <div id="wrap">
      <div class="dd">
        <div class="deng red"></div>
        <div class="deng yellow"></div>
        <div class="deng green"></div>
      </div>
      <div class="time"></div>
    </div>

    <script>
      class DDD {
        constructor(arr) {
          this._arr = arr;
          this.currentIndex = 0;
          this.time = Date.now();
        }

        _update() {
          /**
           * 核心代码
           * 1. 通过当前时间 - 一开始的time 获取还剩余多少时间,并且转为秒
           * 2. 计算得出所有红绿灯的总秒数
           * 3. 剩余时间假设是15秒,总数是10秒,那么相当于就是走了5秒,所以可以通过取余来得出
           * 4. 假设当前剩余时间是5秒,进入循环后,每次都要相减
           *    1. 第一次减去 red 3秒,还剩2秒
           *    2. 将time+=red 3秒,将当前灯改为第二个灯
           *    3. 第二次减去 yellow 2秒,还剩0秒,然后退出循环
           *    4. 当前灯就是 yellow 灯
           * **/

          let surplus = (Date.now() - this.time) / 1000;
          const total = this._arr.reduce((pre, next) => pre + next.time, 0);
          surplus = surplus % total;
          while (1) {
            surplus -= this._arr[this.currentIndex].time;
            if (surplus < 0) break;
            else {
              this.time += this._arr[this.currentIndex].time * 1000;

              this.currentIndex = (this.currentIndex + 1) % this._arr.length;
            }
          }
        }

        getCurrent() {
          this._update();
          const current = this._arr[this.currentIndex];
          const value = Math.floor((Date.now() - this.time) / 1000);
          // 采用问询的模式,返回当前的 颜色以及剩余时间
          return {
            color: current?.color,
            time: current?.time - value,
          };
        }
      }

      const d = new DDD([
        {
          color: 'red',
          time: 3,
        },
        {
          color: 'yellow',
          time: 2,
        },
        {
          color: 'green',
          time: 5,
        },
      ]);

      const dd = document.querySelector('.dd');
      const time = document.querySelector('.time');

      function repo() {
        const current = d.getCurrent();
        dd.className = `dd ${current.color}`;
        time.textContent = current.time;
        requestAnimationFrame(repo);
      }

      repo();

      window.update = d.getCurrent.bind(d);
    </script>
  </body>
</html>
